]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Implement window menus in gnome-shell
authorJasper St. Pierre <jstpierre@mecheye.net>
Thu, 13 Mar 2014 22:51:10 +0000 (18:51 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Sat, 17 May 2014 19:17:29 +0000 (15:17 -0400)
https://bugzilla.gnome.org/show_bug.cgi?id=726352

js/js-resources.gresource.xml
js/ui/windowManager.js
js/ui/windowMenu.js [new file with mode: 0644]
src/gnome-shell-plugin.c
src/shell-wm-private.h
src/shell-wm.c

index 6d9d58529760a767134564d32f8b8656a1272cfb..47bdd0048aba9d90c00563173c6069fd2c1aa640 100644 (file)
@@ -84,6 +84,7 @@
     <file>ui/userWidget.js</file>
     <file>ui/viewSelector.js</file>
     <file>ui/windowAttentionHandler.js</file>
+    <file>ui/windowMenu.js</file>
     <file>ui/windowManager.js</file>
     <file>ui/workspace.js</file>
     <file>ui/workspaceSwitcherPopup.js</file>
index ae6a20717136776eb006d121fcdf79cd60b4fb83..0acf4f609dab512efe61348204cf2ff3574ecda9 100644 (file)
@@ -15,6 +15,7 @@ const WorkspaceSwitcherPopup = imports.ui.workspaceSwitcherPopup;
 const Main = imports.ui.main;
 const ModalDialog = imports.ui.modalDialog;
 const Tweener = imports.ui.tweener;
+const WindowMenu = imports.ui.windowMenu;
 
 const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
 const WINDOW_ANIMATION_TIME = 0.25;
@@ -480,6 +481,7 @@ const WindowManager = new Lang.Class({
         this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace));
         this._shellwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview));
         this._shellwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview));
+        this._shellwm.connect('show-window-menu', Lang.bind(this, this._showWindowMenu));
         this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow));
         this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow));
         this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow));
@@ -669,6 +671,8 @@ const WindowManager = new Lang.Class({
                 this._dimWindow(this._dimmedWindows[i]);
         }));
 
+        this._windowMenuManager = new WindowMenu.WindowMenuManager();
+
         if (Main.sessionMode.hasWorkspaces)
             this._workspaceTracker = new WorkspaceTracker(this);
 
@@ -1159,6 +1163,10 @@ const WindowManager = new Lang.Class({
         this._tilePreview.hide();
     },
 
+    _showWindowMenu: function(shellwm, window) {
+        this._windowMenuManager.showForWindow(window);
+    },
+
     _startAppSwitcher : function(display, screen, window, binding) {
         /* prevent a corner case where both popups show up at once */
         if (this._workspaceSwitcherPopup != null)
diff --git a/js/ui/windowMenu.js b/js/ui/windowMenu.js
new file mode 100644 (file)
index 0000000..6738277
--- /dev/null
@@ -0,0 +1,145 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*
+
+const Gtk = imports.gi.Gtk;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const St = imports.gi.St;
+const Shell = imports.gi.Shell;
+
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const PopupMenu = imports.ui.popupMenu;
+
+const WindowMenu = new Lang.Class({
+    Name: 'WindowMenu',
+    Extends: PopupMenu.PopupMenu,
+
+    _init: function(window) {
+        this.parent(Main.layoutManager.dummyCursor, 0, St.Side.TOP);
+
+        this.actor.add_style_class_name('window-menu');
+
+        Main.layoutManager.uiGroup.add_actor(this.actor);
+        this.actor.hide();
+
+        this._buildMenu(window);
+    },
+
+    _buildMenu: function(window) {
+        let type = window.get_window_type();
+
+        let item;
+
+        item = this.addAction(_("Minimize"), Lang.bind(this, function(event) {
+            window.minimize();
+        }));
+        if (!window.can_minimize())
+            item.setSensitive(false);
+
+        if (window.get_maximized()) {
+            item = this.addAction(_("Unmaximize"), Lang.bind(this, function() {
+                window.unmaximize(Meta.MaximizeFlags.BOTH);
+            }));
+        } else {
+            item = this.addAction(_("Maximize"), Lang.bind(this, function() {
+                window.maximize(Meta.MaximizeFlags.BOTH);
+            }));
+        }
+        if (!window.can_maximize())
+            item.setSensitive(false);
+
+        item = this.addAction(_("Move"), Lang.bind(this, function(event) {
+            window.begin_grab_op(Meta.GrabOp.KEYBOARD_MOVING, true, event.get_time());
+        }));
+        if (!window.allows_move())
+            item.setSensitive(false);
+
+        item = this.addAction(_("Resize"), Lang.bind(this, function(event) {
+            window.begin_grab_op(Meta.GrabOp.KEYBOARD_RESIZING_UNKNOWN, true, event.get_time());
+        }));
+        if (!window.allows_resize())
+            item.setSensitive(false);
+
+        if (!window.titlebar_is_onscreen() && type != Meta.WindowType.DOCK && type != Meta.WindowType.DESKTOP) {
+            this.addAction(_("Move Titlebar Onscreen"), Lang.bind(this, function(event) {
+                window.shove_titlebar_onscreen();
+            }));
+        }
+
+        item = this.addAction("Always on Top", Lang.bind(this, function() {
+            if (window.is_above())
+                window.unmake_above();
+            else
+                window.make_above();
+        }));
+        if (window.is_above())
+            item.setOrnament(PopupMenu.Ornament.DOT);
+        if (window.get_maximized() ||
+            type == Meta.WindowType.DOCK ||
+            type == Meta.WindowType.DESKTOP ||
+            type == Meta.WindowType.SPLASHSCREEN)
+            item.setSensitive(false);
+
+        if (!Meta.prefs_get_workspaces_only_on_primary() || window.is_on_primary_monitor()) {
+            let isSticky = window.is_on_all_workspaces();
+
+            item = this.addAction(_("Always on Visible Workspace"), Lang.bind(this, function() {
+                if (isSticky)
+                    window.unstick();
+                else
+                    window.stick();
+            }));
+            if (isSticky)
+                item.setOrnament(PopupMenu.Ornament.DOT);
+            if (window.is_always_on_all_workspaces())
+                item.setSensitive(false);
+
+            let nWorkspaces = global.screen.n_workspaces;
+
+            if (!isSticky) {
+                let workspace = window.get_workspace();
+                let idx = workspace.index();
+                if (idx > 0) {
+                    this.addAction(_("Move to Workspace Up"), Lang.bind(this, function(event) {
+                        window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.UP));
+                    }));
+                }
+                if (idx < nWorkspaces) {
+                     this.addAction(_("Move to Workspace Down"), Lang.bind(this, function(event) {
+                        window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.DOWN));
+                    }));
+                }
+            }
+        }
+
+        this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+        item = this.addAction(_("Close"), Lang.bind(this, function(event) {
+            window.close(event.get_time());
+        }));
+        if (!window.can_close())
+            item.setSensitive(false);
+    }
+});
+
+const WindowMenuManager = new Lang.Class({
+    Name: 'WindowMenuManager',
+
+    _init: function() {
+        this._manager = new PopupMenu.PopupMenuManager({ actor: Main.layoutManager.dummyCursor });
+    },
+
+    showForWindow: function(window) {
+        let menu = new WindowMenu(window);
+        this._manager.addMenu(menu);
+
+        let [x, y] = global.get_pointer();
+        Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
+        menu.open(BoxPointer.PopupAnimation.NONE);
+        menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
+        menu.connect('open-state-changed', Lang.bind(this, function(menu_, isOpen) {
+            if (!isOpen)
+                menu.destroy();
+        }));
+    },
+});
index 71503bca03adf3d1b20e51c3464600e7b49d1592..75a3bddf0e885760f5c148f3bf72895cf3d9cf81 100644 (file)
@@ -73,7 +73,8 @@ static void gnome_shell_plugin_show_tile_preview (MetaPlugin      *plugin,
                                                   MetaRectangle   *tile_rect,
                                                   int              tile_monitor);
 static void gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin);
-
+static void gnome_shell_plugin_show_window_menu  (MetaPlugin *plugin,
+                                                  MetaWindow *window);
 
 static gboolean              gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
                                                                XEvent     *event);
@@ -140,6 +141,7 @@ gnome_shell_plugin_class_init (GnomeShellPluginClass *klass)
 
   plugin_class->show_tile_preview = gnome_shell_plugin_show_tile_preview;
   plugin_class->hide_tile_preview = gnome_shell_plugin_hide_tile_preview;
+  plugin_class->show_window_menu = gnome_shell_plugin_show_window_menu;
 
   plugin_class->xevent_filter     = gnome_shell_plugin_xevent_filter;
   plugin_class->keybinding_filter = gnome_shell_plugin_keybinding_filter;
@@ -303,6 +305,13 @@ gnome_shell_plugin_hide_tile_preview (MetaPlugin *plugin)
   _shell_wm_hide_tile_preview (get_shell_wm ());
 }
 
+static void
+gnome_shell_plugin_show_window_menu (MetaPlugin *plugin,
+                                     MetaWindow *window)
+{
+  _shell_wm_show_window_menu (get_shell_wm (), window);
+}
+
 static gboolean
 gnome_shell_plugin_xevent_filter (MetaPlugin *plugin,
                                   XEvent     *xev)
index 9df4adab34b9e1096314d74ba4018afaa447304d..35cbcb0f7d8cc3cae892ef433e70583c5ce3e716 100644 (file)
@@ -40,6 +40,8 @@ void _shell_wm_show_tile_preview     (ShellWM             *wm,
                                       MetaRectangle       *tile_rect,
                                       int                  tile_monitor);
 void _shell_wm_hide_tile_preview     (ShellWM             *wm);
+void _shell_wm_show_window_menu      (ShellWM             *wm,
+                                      MetaWindow          *window);
 
 gboolean _shell_wm_filter_keybinding (ShellWM             *wm,
                                       MetaKeyBinding      *binding);
index f2c484d6d0c496babdc62422fd8044bbb28f9668..0d51606d771e2fab5a4d8c675324fdb644ec21ca 100644 (file)
@@ -28,6 +28,7 @@ enum
   KILL_WINDOW_EFFECTS,
   SHOW_TILE_PREVIEW,
   HIDE_TILE_PREVIEW,
+  SHOW_WINDOW_MENU,
   FILTER_KEYBINDING,
   CONFIRM_DISPLAY_CHANGE,
 
@@ -135,6 +136,13 @@ shell_wm_class_init (ShellWMClass *klass)
                   0,
                   NULL, NULL, NULL,
                   G_TYPE_NONE, 0);
+  shell_wm_signals[SHOW_WINDOW_MENU] =
+    g_signal_new ("show-window-menu",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 1,
+                  META_TYPE_WINDOW);
   shell_wm_signals[FILTER_KEYBINDING] =
     g_signal_new ("filter-keybinding",
                   G_TYPE_FROM_CLASS (klass),
@@ -288,6 +296,12 @@ _shell_wm_hide_tile_preview (ShellWM *wm)
   g_signal_emit (wm, shell_wm_signals[HIDE_TILE_PREVIEW], 0);
 }
 
+void
+_shell_wm_show_window_menu (ShellWM    *wm,
+                            MetaWindow *window)
+{
+  g_signal_emit (wm, shell_wm_signals[SHOW_WINDOW_MENU], 0, window);
+}
 
 void
 _shell_wm_minimize (ShellWM         *wm,